#include "StdAfx.h"
#include "AiBrain.h"
#include "AiPlayer.h"
#include <stdlib.h>


//--------------------------------------------------------------------------------------------------------------------------------
//PreFlopTable
//--------------------------------------------------------------------------------------------------------------------------------
#pragma region PreFlopTable
AiBrain::PreFlopTable::PreFlopTable()
{
	//init table
	FillEarlyPhase();
	FillMiddlePhase();
	FillLatePhase();
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::PreFlopTable::FillEarlyPhase()
{
	//fill early actions
	std::map<std::string, ActionMatrix> &m = A[PHASE_EARLY];
	ActionMatrix matrix;

	//AA, KK
	matrix.Action[RAISE_NO][POS_EARLY] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_LATE] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_ALL_IN;

	m["AA"] = matrix;
	m["KK"] = matrix;


	//QQ
	matrix.Action[RAISE_NO][POS_EARLY] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_LATE] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["QQ"] = matrix;


	//AK
	matrix.Action[RAISE_NO][POS_EARLY]  = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]   = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY]  = ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]   = ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY]  = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]   = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["AK"] = matrix;


	//JJ, TT
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]= ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["JJ"] = matrix;
	m["TT"] = matrix;


	//99, 88, 77..22
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]= ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["22"] = matrix;
	m["33"] = matrix;
	m["44"] = matrix;
	m["55"] = matrix;
	m["66"] = matrix;
	m["77"] = matrix;
	m["88"] = matrix;
	m["99"] = matrix;


	//AQ, AJ, KQ
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]= ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["AQ"] = matrix;
	m["AJ"] = matrix;
	m["KQ"] = matrix;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::PreFlopTable::FillMiddlePhase()
{
	//fill early actions
	std::map<std::string, ActionMatrix> &m = A[PHASE_MIDDLE];
	ActionMatrix matrix;


	//AA, KK
	matrix.Action[RAISE_NO][POS_EARLY]  = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY]  = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_LATE]   = ACT_RAISE;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY]  = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]   = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_ALL_IN;

	m["AA"] = matrix;
	m["KK"] = matrix;


	//AK, QQ, JJ
	matrix.Action[RAISE_NO][POS_EARLY]  = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]   = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY]  = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_LATE]	  = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_ALL_IN;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY]  = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]	= ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["AK"] = matrix;
	m["QQ"] = matrix;
	m["JJ"] = matrix;


	//AQ, TT
	matrix.Action[RAISE_NO][POS_EARLY]  = ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 = ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] = ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]= ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  = ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]= ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]= ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS]= ACT_FOLD;

	m["JJ"] = matrix;
	m["TT"] = matrix;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::PreFlopTable::FillLatePhase()
{
	//fill early actions
	std::map<std::string, ActionMatrix> &m = A[PHASE_LATE];
	ActionMatrix matrix;

	//AA, KK, QQ
	matrix.Action[RAISE_NO][POS_EARLY] = ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_LATE] = ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_ALL_IN;

	matrix.Action[RAISE_ONE][POS_EARLY] = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_LATE] = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_ALL_IN;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE] = ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_ALL_IN;

	m["AA"] = matrix;
	m["KK"] = matrix;
	m["QQ"] = matrix;


	//AK
	matrix.Action[RAISE_NO][POS_EARLY]  = ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_LATE]   = ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_BLINDS] = ACT_ALL_IN;

	matrix.Action[RAISE_ONE][POS_EARLY]  = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_MIDDLE] = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_LATE]   = ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_BLINDS] = ACT_ALL_IN;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY]  = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE] = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]   = ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["AK"] = matrix;


	//JJ, TT
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_ALL_IN;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_ALL_IN;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_ALL_IN;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_ALL_IN;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_ALL_IN;

	m["JJ"] = matrix;
	m["TT"] = matrix;


	//99, 88, 77..22
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_ALL_IN;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS]=  ACT_FOLD;

	m["22"] = matrix;
	m["33"] = matrix;
	m["44"] = matrix;
	m["55"] = matrix;
	m["66"] = matrix;
	m["77"] = matrix;
	m["88"] = matrix;
	m["99"] = matrix;


	//AQ, AJ, KQ
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_LATE]	=	ACT_ALL_IN;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_ALL_IN;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	m["AQ"] = matrix;
	m["AJ"] = matrix;
	m["KQ"] = matrix;
}
#pragma endregion

//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::Actions AiBrain::PreFlopTable::GetAction(AiBrain *iBrain, bool iPreflopRaise, int &oRaiseCnt)
{
	//raises 
	if(iBrain->Phase < PHASE_LATE && 
		((iBrain->Phase == PHASE_EARLY && iBrain->MaxRaiseCount > 6) || iBrain->WasRaisesAfterUs))
	{
		if(iBrain->Cards == "AA" || iBrain->Cards == "KK" || iBrain->Cards == "QQ" || iBrain->Cards == "AK" || iBrain->Cards == "KA")
		{
			return ACT_ALL_IN;
		}
		else
		{
			return ACT_FOLD;
		}
	}


	//no record in table - default action is ACT_FOLD 
	std::map<std::string, ActionMatrix> &m  =  A[iBrain->Phase];
	AiBrain::Actions  a = ACT_FOLD;
	std::map<std::string, ActionMatrix>::iterator it = A[iBrain->Phase].find(iBrain->Cards);
	if(it != A[iBrain->Phase].end())
		a = it->second.Action[iBrain->Raise][iBrain->Pos];

	//raise count
	if(iBrain->WasRaisesAfterUs == false && iBrain->Raise == RAISE_NO)
	{
		//2-4 big blinds
		int r = int(((double)rand()/RAND_MAX)*2 + 0.5);
		oRaiseCnt = iBrain->Pl->TableInfo->BigBlind * (2 + r);
	}
	else 
	{
		//3-6 big blinds + last raise count
		int r = int(((double)rand()/RAND_MAX)*3 + 0.5);
		oRaiseCnt = iBrain->LastRaiseCount + iBrain->Pl->TableInfo->BigBlind * (3 + r);
	}

	if(a == ACT_RAISE && oRaiseCnt > iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stack/2)
		return ACT_ALL_IN;

	//check if no stakes on table or if our stake is the last stake
// 	if(a == ACT_FOLD && (iBrain->LastRaiseCount == 0 || iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stake == iBrain->MaxStake) )
// 		return ACT_CHECK;

	return a;
}


//--------------------------------------------------------------------------------------------------------------------------------
//FlopTable
//--------------------------------------------------------------------------------------------------------------------------------
#pragma region FlopTable
AiBrain::FlopTable::FlopTable()
{
	//init table
	FillEarlyPhase();
	FillMiddlePhase();
	FillLatePhase();
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::FlopTable::FillEarlyPhase()
{
	ActionMatrix matrix;

	//over-pair, top-pair, other with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithRaise[PHASE_EARLY][HAND_OVER_PAIR] = matrix;
	AmsWithRaise[PHASE_EARLY][HAND_TOP_PAIR] = matrix;
	AmsWithRaise[PHASE_EARLY][HAND_TWO_PAIRS_OR_MORE] = matrix;



	//without raise, over-pair, top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithoutRaise[PHASE_EARLY][HAND_OVER_PAIR] = matrix;
	AmsWithoutRaise[PHASE_EARLY][HAND_TOP_PAIR] = matrix;


	//without raise, two or more
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithoutRaise[PHASE_EARLY][HAND_TWO_PAIRS_OR_MORE] = matrix;

}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::FlopTable::FillMiddlePhase()
{
	ActionMatrix matrix;

	//over-pair, top-pair, other with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithRaise[PHASE_MIDDLE][HAND_OVER_PAIR] = matrix;
	AmsWithRaise[PHASE_MIDDLE][HAND_TOP_PAIR] = matrix;
	AmsWithRaise[PHASE_MIDDLE][HAND_TWO_PAIRS_OR_MORE] = matrix;



	//without raise, over-pair, top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithoutRaise[PHASE_MIDDLE][HAND_OVER_PAIR] = matrix;
	AmsWithoutRaise[PHASE_MIDDLE][HAND_TOP_PAIR] = matrix;


	//without raise, two or more
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithoutRaise[PHASE_MIDDLE][HAND_TWO_PAIRS_OR_MORE] = matrix;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::FlopTable::FillLatePhase()
{
	ActionMatrix matrix;

	//over-pair, top-pair, other with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_FOLD;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	AmsWithRaise[PHASE_LATE][HAND_OVER_PAIR] = matrix;
	AmsWithRaise[PHASE_LATE][HAND_TOP_PAIR] = matrix;
	AmsWithRaise[PHASE_LATE][HAND_TWO_PAIRS_OR_MORE] = matrix;

	AmsWithoutRaise[PHASE_LATE][HAND_OVER_PAIR] = matrix;
	AmsWithoutRaise[PHASE_LATE][HAND_TOP_PAIR] = matrix;
	AmsWithoutRaise[PHASE_LATE][HAND_TWO_PAIRS_OR_MORE] = matrix;
}

#pragma endregion


//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::Actions AiBrain::FlopTable::GetAction(AiBrain *iBrain, bool iPreflopRaise, int &oRaiseCnt)
{
	//raises 
	if(iBrain->Phase < PHASE_LATE && iBrain->MaxRaiseCount > 6)
	{
		//play >= over pair
		if(iBrain->Hand < HAND_OVER_PAIR) return ACT_FOLD;
	}


	//raise count
	if(iBrain->WasRaisesAfterUs == false && iBrain->Raise == RAISE_NO)
	{
		//2-4 big blinds
		int r = int(((double)rand()/RAND_MAX)*2 + 0.5);
		oRaiseCnt = iBrain->Pl->TableInfo->BigBlind * (2 + r);
	}
	else 
	{
		//3-6 big blinds + last raise count
		int r = int(((double)rand()/RAND_MAX)*3 + 0.5);
		oRaiseCnt = iBrain->LastRaiseCount + iBrain->Pl->TableInfo->BigBlind * (3 + r);
	}

	
	//no record in table - default action is ACT_FOLD 
	AiBrain::Actions a = ACT_FOLD;

	if(iBrain->Hand != HAND_NOTHING)
	{
		if(iPreflopRaise)
		{
			a = AmsWithRaise[iBrain->Phase][iBrain->Hand].Action[iBrain->Raise][iBrain->Pos];	
		}
		else
		{
			a = AmsWithoutRaise[iBrain->Phase][iBrain->Hand].Action[iBrain->Raise][iBrain->Pos];	
		}	
	}

	if(a == ACT_RAISE && oRaiseCnt > iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stack/2)
		return ACT_ALL_IN;

// 	if(a == ACT_FOLD && (iBrain->LastRaiseCount == 0 || iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stake == iBrain->MaxStake))
// 		return ACT_CHECK;

	return a;
}

//--------------------------------------------------------------------------------------------------------------------------------
//TurnTable
//--------------------------------------------------------------------------------------------------------------------------------
#pragma region TurnTable
AiBrain::TurnTable::TurnTable()
{
	//init table
	FillEarlyPhase();
	FillMiddlePhase();
	FillLatePhase();
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::TurnTable::FillEarlyPhase()
{
	ActionMatrix matrix;

	//over-pair with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithRaise[PHASE_EARLY][HAND_OVER_PAIR] = matrix;


	//top-pair with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	AmsWithRaise[PHASE_EARLY][HAND_TOP_PAIR] = matrix;
	
	
	//two-pairs or more with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithRaise[PHASE_EARLY][HAND_TWO_PAIRS_OR_MORE] = matrix;



	//without raise, over-pair, top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	AmsWithoutRaise[PHASE_EARLY][HAND_OVER_PAIR] = matrix;
	AmsWithoutRaise[PHASE_EARLY][HAND_TOP_PAIR] = matrix;


	//without raise, two or more
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithoutRaise[PHASE_EARLY][HAND_TWO_PAIRS_OR_MORE] = matrix;

}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::TurnTable::FillMiddlePhase()
{
	ActionMatrix matrix;

	//over-pair with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithRaise[PHASE_MIDDLE][HAND_OVER_PAIR] = matrix;


	//top-pair with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	AmsWithRaise[PHASE_MIDDLE][HAND_TOP_PAIR] = matrix;


	//two-pairs or more with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithRaise[PHASE_MIDDLE][HAND_TWO_PAIRS_OR_MORE] = matrix;



	//without raise, over-pair, top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	AmsWithoutRaise[PHASE_MIDDLE][HAND_OVER_PAIR] = matrix;
	AmsWithoutRaise[PHASE_MIDDLE][HAND_TOP_PAIR] = matrix;


	//without raise, two or more
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	AmsWithoutRaise[PHASE_MIDDLE][HAND_TWO_PAIRS_OR_MORE] = matrix;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::TurnTable::FillLatePhase()
{
	ActionMatrix matrix;

	//over-pair, top-pair, other with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_FOLD;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	AmsWithRaise[PHASE_LATE][HAND_OVER_PAIR] = matrix;
	AmsWithRaise[PHASE_LATE][HAND_TOP_PAIR] = matrix;
	AmsWithRaise[PHASE_LATE][HAND_TWO_PAIRS_OR_MORE] = matrix;

	AmsWithoutRaise[PHASE_LATE][HAND_OVER_PAIR] = matrix;
	AmsWithoutRaise[PHASE_LATE][HAND_TOP_PAIR] = matrix;
	AmsWithoutRaise[PHASE_LATE][HAND_TWO_PAIRS_OR_MORE] = matrix;
}
#pragma endregion

//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::Actions AiBrain::TurnTable::GetAction(AiBrain *iBrain, bool iPreflopRaise, int &oRaiseCnt)
{
	//raises 
	if(iBrain->Phase < PHASE_LATE && iBrain->MaxRaiseCount > 6)
	{
		//play >= over pair
		if(iBrain->Hand < HAND_OVER_PAIR) return ACT_FOLD;
	}


	//raise count
	//raise count
	if(iBrain->WasRaisesAfterUs == false && iBrain->Raise == RAISE_NO)
	{
		//2-4 big blinds
		int r = int(((double)rand()/RAND_MAX)*2 + 0.5);
		oRaiseCnt = iBrain->Pl->TableInfo->BigBlind * (2 + r);
	}
	else 
	{
		//3-6 big blinds + last raise count
		int r = int(((double)rand()/RAND_MAX)*3 + 0.5);
		oRaiseCnt = iBrain->LastRaiseCount + iBrain->Pl->TableInfo->BigBlind * (3 + r);
	}


	//no record in table - default action is ACT_FOLD 
	AiBrain::Actions a = ACT_FOLD;

	if(iBrain->Hand != HAND_NOTHING)
	{
		if(iPreflopRaise)
			a = AmsWithRaise[iBrain->Phase][iBrain->Hand].Action[iBrain->Raise][iBrain->Pos];	
		else
			a = AmsWithoutRaise[iBrain->Phase][iBrain->Hand].Action[iBrain->Raise][iBrain->Pos];	
	}

	if(a == ACT_RAISE && oRaiseCnt > iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stack/2)
		return ACT_ALL_IN;

// 	if(a == ACT_FOLD && (iBrain->LastRaiseCount == 0 || iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stake == iBrain->MaxStake))
// 		return ACT_CHECK;

	return a;
}

//--------------------------------------------------------------------------------------------------------------------------------
//RiverTable
//--------------------------------------------------------------------------------------------------------------------------------
#pragma region RiverTable
AiBrain::RiverTable::RiverTable()
{
	//init table
	FillEarlyPhase();
	FillMiddlePhase();
	FillLatePhase();
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::RiverTable::FillEarlyPhase()
{
	ActionMatrix matrix;

	//over-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	Ams[PHASE_EARLY][HAND_OVER_PAIR] = matrix;


	//top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	Ams[PHASE_EARLY][HAND_TOP_PAIR] = matrix;


	//top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	Ams[PHASE_EARLY][HAND_TWO_PAIRS_OR_MORE] = matrix;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::RiverTable::FillMiddlePhase()
{
	ActionMatrix matrix;

	//over-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	Ams[PHASE_MIDDLE][HAND_OVER_PAIR] = matrix;


	//top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_CALL;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_CALL;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	Ams[PHASE_MIDDLE][HAND_TOP_PAIR] = matrix;


	//top-pair
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_RAISE;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_RAISE;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_CALL;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_CALL;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_CALL;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_CALL;

	Ams[PHASE_MIDDLE][HAND_TWO_PAIRS_OR_MORE] = matrix;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::RiverTable::FillLatePhase()
{
	ActionMatrix matrix;

	//over-pair, top-pair, other with raise
	matrix.Action[RAISE_NO][POS_EARLY]  =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_MIDDLE] =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_LATE]	 =	ACT_FOLD;
	matrix.Action[RAISE_NO][POS_BLINDS] =	ACT_FOLD;

	matrix.Action[RAISE_ONE][POS_EARLY] =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_LATE]  =	ACT_FOLD;
	matrix.Action[RAISE_ONE][POS_BLINDS]=  ACT_FOLD;

	matrix.Action[RAISE_MORE_THAN_ONE][POS_EARLY] =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_MIDDLE]=  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_LATE]  =  ACT_FOLD;
	matrix.Action[RAISE_MORE_THAN_ONE][POS_BLINDS] = ACT_FOLD;

	Ams[PHASE_LATE][HAND_OVER_PAIR] = matrix;
	Ams[PHASE_LATE][HAND_TOP_PAIR] = matrix;
	Ams[PHASE_LATE][HAND_TWO_PAIRS_OR_MORE] = matrix;	
}
#pragma endregion

//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::Actions AiBrain::RiverTable::GetAction(AiBrain *iBrain, bool iPreflopRaise, int &oRaiseCnt)
{
	//raises 
	if(iBrain->Phase < PHASE_LATE && iBrain->MaxRaiseCount > 6)
	{
		//play >= over pair
		if(iBrain->Hand < HAND_OVER_PAIR) return ACT_FOLD;
	}


	//raise count
	if(iBrain->WasRaisesAfterUs == false && iBrain->Raise == RAISE_NO)
	{
		//2-4 big blinds
		int r = int(((double)rand()/RAND_MAX)*2 + 0.5);
		oRaiseCnt = iBrain->Pl->TableInfo->BigBlind * (2 + r);
	}
	else 
	{
		//3-6 big blinds + last raise count
		int r = int(((double)rand()/RAND_MAX)*3 + 0.5);
		oRaiseCnt = iBrain->LastRaiseCount + iBrain->Pl->TableInfo->BigBlind * (3 + r);
	}


	//no record in table - default action is ACT_FOLD 
	AiBrain::Actions a = ACT_FOLD;
	
	if(iBrain->Hand != HAND_NOTHING) a = Ams[iBrain->Phase][iBrain->Hand].Action[iBrain->Raise][iBrain->Pos];	
	
	if(a == ACT_RAISE && oRaiseCnt > iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stack/2)
		return ACT_ALL_IN;
	

// 	if(a == ACT_FOLD && (iBrain->LastRaiseCount == 0 || iBrain->Pl->PlayerInfo[iBrain->Pl->MyPos].Stake == iBrain->MaxStake))
// 		return ACT_CHECK;

	return a;
}

//--------------------------------------------------------------------------------------------------------------------------------
//AiBrain
//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::AiBrain()
{
	PreflopRaise = false;
	Cards = "  ";

	//cards priority
	ZeroMemory(CardStack, 600*sizeof(char));

	CardStack['2'] = 0;
	CardStack['3'] = 1;
	CardStack['4'] = 2;
	CardStack['5'] = 3;
	CardStack['6'] = 4;
	CardStack['7'] = 5;
	CardStack['8'] = 6;
	CardStack['9'] = 7;
	CardStack['T'] = 8;
	CardStack['t'] = 8;
	CardStack['J'] = 9;
	CardStack['j'] = 9;
	CardStack['Q'] = 10;
	CardStack['q'] = 10;
	CardStack['K'] = 11;
	CardStack['k'] = 11;
	CardStack['a'] = 12;
	CardStack['A'] = 12;

	IsFirstRound = false;
	FirstTime = true;

	Combintation = COMB_NOTHING;
	HandCardsFromCombination = 0;

	//allocate table
	Tables[STAGE_PREFLOP]= new PreFlopTable();
	Tables[STAGE_FLOP]	 = new FlopTable();
	Tables[STAGE_RIVER]	 = new RiverTable();
	Tables[STAGE_TURN]	 = new TurnTable();
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DeterminePhase()
{
	int st = Pl->PlayerInfo[Pl->MyPos].Stack;
	int bb = Pl->TableInfo->BigBlind;

	//my stack > bb*24 - early phase
	if(st > bb * 24)
		Phase = PHASE_EARLY;
	else if(st > bb * 13)
		Phase = PHASE_MIDDLE;
	else Phase = PHASE_LATE;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DetermineStage()
{
	size_t cn = Pl->TableInfo->Cards.size();
	//no cards - preflop
	if(cn < 2)
		Stage = STAGE_PREFLOP;
	//3 cards - flop
	else if(cn < 10)
		Stage = STAGE_FLOP;	
	//4 cards - turn
	else if(cn < 13)
		Stage = STAGE_TURN;
	else
	//5 cards - river
		Stage = STAGE_RIVER;		
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DeterminePosition()
{
	int bpos = Pl->TableInfo->ButtonPos;
	int mp = Pl->MyPos;
	
	//shift button position by 2 players
	for(int n=0;;)
	{
		bpos++;
		if(bpos >= NUM_PLAYERS) bpos = 0;
		if(Pl->PlayerInfo[bpos].Active)
			n++;
		if(n >= 2) break;
	}

	//determine real position of player
	//excluding non-active players
	int realpos = NUM_PLAYERS;
	int border = bpos + 1 >= NUM_PLAYERS ? bpos+1 - NUM_PLAYERS : bpos + 1;
	for(int i=bpos; ; i--)
	{
		if(i < 0)
		{
			i = NUM_PLAYERS-1;			
		}

		if(Pl->PlayerInfo[i].Active)
			realpos--;

		if(i == border || i == mp) break;
	}
	
	//determine phase
	if(realpos < 3) Pos = POS_EARLY;
	else if(realpos < 6) Pos = POS_MIDDLE;
	else if(realpos < 8) Pos  = POS_LATE;
	else Pos = POS_BLINDS;
	
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DetermineRaises()
{	  
	int start = 0;
	int aplayer = 0;
	int button_pos = Pl->TableInfo->ButtonPos;
	

// 	ColdCollersCount = 0;
// 	LimpersCount = 0;

	//determine raises
	int last_raise = Pl->TableInfo->BigBlind;
	int max_stake = last_raise;
	bool any_active = false;
	int raises_before = 0;
	int raises_after = 0;
	//get position relative to button
	int my_pos = Pl->MyPos - button_pos;
	if(my_pos < 0) my_pos += NUM_PLAYERS;

	for(int i=button_pos + 1; ; i++)
	{
		if(i >= NUM_PLAYERS)
		{
			i = 0;
		}

		//get position relative to button
		int pos = i - button_pos;
		if(pos < 0) pos += NUM_PLAYERS;
		if(Pl->PlayerInfo[i].Active)
		{			
			if((last_raise < Pl->PlayerInfo[i].Stake || 
				!any_active) && Pl->PlayerInfo[i].Stake >= Pl->TableInfo->BigBlind
				&& Pl->PlayerInfo[i].Stake > Pl->PlayerInfo[Pl->MyPos].Stake)
			{
				last_raise = Pl->PlayerInfo[i].Stake;
				
				//raises before us
				if(IsFirstRound)
				{
					if(pos < my_pos)
					{
						 raises_before++;
						
					}
					//raises after us
					else if(pos > my_pos)
					{
						//increase ColdCallers count
	//					ColdCollersCount++;
						raises_after++;
					}
				}
				else raises_after++;
			}
// 			//cold callers and limpers
// 			if(pos < my_pos)
// 			{
// 				if(Pl->PlayerInfo[i].Stake > Pl->TableInfo->BigBlind)
// 					LimpersCount++; //sit before us and raise
// 			}
// 			else if(pos > my_pos)
// 			{
// 				ColdCollersCount++;
// 			}	
			if(Pl->PlayerInfo[i].Stake >= Pl->TableInfo->SmallBlind) any_active = true;

			//for check
		//	if(Pl->PlayerInfo[i].Stake > Pl->TableInfo->SmallBlind && Pl->PlayerInfo[i].Stake > max_stake) max_stake = Pl->PlayerInfo[i].Stake;
		}	
		

		if(i == button_pos)
			break;
	}


	//fill info
	if(raises_before == 0)
		Raise = RAISE_NO;
	else if(raises_before == 1)
		Raise = RAISE_ONE;
	else Raise = RAISE_MORE_THAN_ONE;
	LastRaiseCount = any_active ? last_raise : 0;
//	MaxStake = any_active ? max_stake : 0;
	MaxRaiseCount = last_raise/Pl->TableInfo->BigBlind;
	WasRaisesAfterUs = raises_after > 0;	
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DetermineCards()
{
	int mp = Pl->MyPos;
	if(Pl->PlayerInfo[mp].Cards.size() < 3) return;
	
	std::locale::global(std::locale(""));
	
	//fill hand
	Cards.resize(2);
	Suits.resize(2);

	Cards[0] = toupper(Pl->PlayerInfo[mp].Cards[0]);
	Suits[0] = toupper(Pl->PlayerInfo[mp].Cards[1]);
	Cards[1] = toupper(Pl->PlayerInfo[mp].Cards[3]);
	Suits[1] = toupper(Pl->PlayerInfo[mp].Cards[4]);
	Cards[2] = 0;
	Suits[2] = 0;

	
	//sort
	if(Cards[0] > Cards[1])
	{
		char b1 = Cards[0];
		char b2 = Suits[0];
		Cards[0] = Cards[1];
		Suits[0] = Suits[1];
		Cards[1] = b1;
		Suits[1] = b2;
	}
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::SortCards(char c[100], char s[100], int player_cards[2], int n)
{
	//sort
	for(int i=0; i < n; i++)
	{           
		for(int j = n-1; j > i; j-- )
		{
			if (CardStack[c[j-1]] > CardStack[c[j]])
			{
				char x = c[j-1];
				c[j-1] = c[j];
				c[j] = x;

				x = s[j-1];
				s[j-1] = s[j];
				s[j] = x;

				if(j-1 == player_cards[0])
					player_cards[0] = j;
				else if(j == player_cards[0])
					player_cards[0] = j-1;
				if(j-1 == player_cards[1])
					player_cards[1] = j;
				else if(j == player_cards[1])
					player_cards[1] = j-1;
			}
		}
	}
}


//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::CombinationInfo AiBrain::GatherCombinationInfo(char c[100], char s[100], int n)
{
	CombinationInfo result;
	int thesames = 0;
	result.groups2 = 0;
	result.groups3 = 0;
	result.groups4 = 0;
	result.street_cards = 1;
	result.num_hearts = 0;
	result.num_spades = 0;
	result.num_clubs = 0;
	result.num_diamonds = 0;
	result.highest_street_card = c[0];
	result.street_cards_indeces[50];
	result.clubs_cards[7];
	result.diamonds_cards[7];
	result.hearts_cards[7];
	result.spades_cards[7];
	

	if(s[0] == 'S' || s[0] == 's') { result.spades_cards[result.num_spades] = c[0]; result.num_spades++; }
	if(s[0] == 'H' || s[0] == 'h') { result.hearts_cards[result.num_hearts] = c[0]; result.num_hearts++; }
	if(s[0] == 'C' || s[0] == 'c') { result.clubs_cards[result.num_clubs] = c[0]; result.num_clubs++; }
	if(s[0] == 'D' || s[0] == 'd') { result.diamonds_cards[result.num_diamonds] = c[0]; result.num_diamonds++; }

	for(int i=1; i<n; i++)
	{
		//check for street
		if(result.street_cards < 5)
		{
			if(CardStack[c[i]] == CardStack[c[i-1]]+1)
			{
				result.street_cards_indeces[result.street_cards] = i-1;
				result.street_cards++;
				result.street_cards_indeces[result.street_cards] = i;

				result.highest_street_card = c[i];				
			}
			else
				result.street_cards = 1;
		}

		//flush
		if(s[i] == 'S' || s[i] == 's') { result.spades_cards[result.num_spades] = c[i]; result.num_spades++; }
		if(s[i] == 'H' || s[i] == 'h') { result.hearts_cards[result.num_hearts] = c[i]; result.num_hearts++; }
		if(s[i] == 'C' || s[i] == 'c') { result.clubs_cards[result.num_clubs] = c[i]; result.num_clubs++; }
		if(s[i] == 'D' || s[i] == 'd') { result.diamonds_cards[result.num_diamonds] = c[i]; result.num_diamonds++; }


		//groups
		if(c[i] == c[i-1])
		{
			thesames++;
		}
		else
		{
			//count the same cards 
			if(thesames > 0)
			{
				if(thesames == 1)
				{
					result.cards_2[result.groups2] = c[i-1];
					result.groups2++;
				}
				else if(thesames == 2)
				{
					result.cards_3[result.groups3] = c[i-1];
					result.groups3++;					
				}
				else
				{
					result.cards_4[result.groups4] = c[i-1];
					result.groups4++;		
				}
				thesames = 0;
			}
		}
	}

	//last compare
	if(thesames > 0)
	{
		if(thesames == 1)
		{
			result.cards_2[result.groups2] = c[n-1];
			result.groups2++;
		}
		else if(thesames == 2)
		{
			result.cards_3[result.groups3] = c[n-1];
			result.groups3++;					
		}
		else
		{
			result.cards_4[result.groups4] = c[n-1];
			result.groups4++;		
		}
		thesames = 0;
	}
	return result;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::ProcessFlashOrStreet(AiBrain::CombinationInfo &info, int player_cards[2])
{
	//flash?
	bool is_flash = info.num_diamonds >= 5 || info.num_clubs >= 5 || info.num_hearts >= 5 || info.num_spades >= 5;
	HandCardsFromCombination = 0;

	//determine hand cards
	if(info.num_diamonds >= 5)
	{
		if(Suits[0] == 'd' || Suits[0] == 'D') HandCardsFromCombination++;
		if(Suits[1] == 'd' || Suits[1] == 'D') HandCardsFromCombination++;

		//if we have lowest cards of the combination - ignore it
		if(HandCardsFromCombination > 0 && 
			info.num_diamonds == 6 && 
			(Cards[0] == info.diamonds_cards[0] || Cards[1] == info.diamonds_cards[0]))  HandCardsFromCombination--;
		if(HandCardsFromCombination > 0 && 
			info.num_diamonds == 7 && 
			(Cards[0] == info.diamonds_cards[1] || Cards[1] == info.diamonds_cards[1]))  HandCardsFromCombination--;
	}
	else if(info.num_clubs >= 5)
	{
		if(Suits[0] == 'c' || Suits[0] == 'C') HandCardsFromCombination++;
		if(Suits[1] == 'c' || Suits[1] == 'C') HandCardsFromCombination++;

		//if we have lowest cards of the combination - ignore it
		if(HandCardsFromCombination > 0 && 
			info.num_clubs == 6 && 
			(Cards[0] == info.clubs_cards[0] || Cards[1] == info.clubs_cards[0]))  HandCardsFromCombination--;
		if(HandCardsFromCombination > 0 && 
			info.num_clubs == 7 && 
			(Cards[0] == info.clubs_cards[1] || Cards[1] == info.clubs_cards[1]))  HandCardsFromCombination--;
	}
	else if(info.num_hearts >= 5)
	{
		if(Suits[0] == 'h' || Suits[0] == 'H') HandCardsFromCombination++;
		if(Suits[1] == 'h' || Suits[1] == 'H') HandCardsFromCombination++;

		//if we have lowest cards of the combination - ignore it
		if(HandCardsFromCombination > 0 && 
			info.num_hearts == 6 && 
			(Cards[0] == info.hearts_cards[0] || Cards[1] == info.hearts_cards[0]))  HandCardsFromCombination--;
		if(HandCardsFromCombination > 0 && 
			info.num_hearts == 7 && 
			(Cards[0] == info.hearts_cards[1] || Cards[1] == info.hearts_cards[1]))  HandCardsFromCombination--;
	}
	else if(info.num_spades >= 5)
	{
		if(Suits[0] == 's' || Suits[0] == 'S') HandCardsFromCombination++;
		if(Suits[1] == 's' || Suits[1] == 'S') HandCardsFromCombination++;

		//if we have lowest cards of the combination - ignore it
		if(HandCardsFromCombination > 0 && 
			info.num_spades == 6 && 
			(Cards[0] == info.spades_cards[0] || Cards[1] == info.spades_cards[0]))  HandCardsFromCombination--;
		if(HandCardsFromCombination > 0 && 
			info.num_spades == 7 && 
			(Cards[0] == info.spades_cards[1] || Cards[1] == info.spades_cards[1]))  HandCardsFromCombination--;
	}


	bool is_street = info.street_cards >= 5;
	if(is_street && HandCardsFromCombination == 0)
	{
		//check if we have one of the cart on hand
		//one or two smallest cards will be ignored, if street cards > 5
		for(int i=info.street_cards-5; i<info.street_cards; i++)
		{
			if(info.street_cards_indeces[i] == player_cards[0] || info.street_cards_indeces[i] == player_cards[1]) 
				HandCardsFromCombination++;
		}
	}
	//flash street
	if(is_flash && is_street)
	{
		//royal flash
		if(info.highest_street_card == 'a' || info.highest_street_card == 'A')
		{
			Combintation = COMB_ROYALFLASH;
		}
		else
		{
			Combintation = COMB_STREETFLASH;
		}
	}
	else if(is_street) Combintation = COMB_STREET;
	else if(is_flash) Combintation = COMB_FLUSH;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::Process234FullHouse(AiBrain::CombinationInfo &info, int player_cards[2])
{
	if(info.groups4 > 0)
	{
		Combintation = COMB_FOUR;
	}
	else if(info.groups3 > 0 && info.groups2 > 0)
	{
		//full house
		Combintation = COMB_FULLHOUSE;
	}
	else if(info.groups3 > 0)
	{
		Combintation = COMB_THREE;
	}
	else if(info.groups2 > 1)
	{
		Combintation = COMB_TWOPAIRS;
	}

	if(HandCardsFromCombination == 0 && (info.groups3 > 0 || info.groups2 > 0))
	{
		//check if some of the combination cards is on hand
		if(info.groups4 > 0)
		{//4 2 1 or something else - only 4 is important
			if(info.cards_4[0] == Cards[0]) 
				HandCardsFromCombination++;
			if(info.cards_4[0] == Cards[1])
				HandCardsFromCombination++;
		}
		else if(info.groups3 > 0 && info.groups2 == 0)
		{//3 3 1 or something else - check if something on hand
			for(int i=0; i<info.groups3; i++)
			{
				if(info.cards_3[i] == Cards[0]) 
					HandCardsFromCombination++;
				if(info.cards_3[i] == Cards[1])
					HandCardsFromCombination++;
			}
		}
		else if(info.groups3 == 0 && info.groups2 > 0)
		{//2 2 2 - check if lowest 2 is not on hand			
			for(int i=info.groups2-2; i<info.groups2; i++)
			{
				if(info.cards_2[i] == Cards[0]) 
					HandCardsFromCombination++;
				if(info.cards_2[i] == Cards[1])
					HandCardsFromCombination++;
			}
		}
		else
		{//2 2 3 - three is anyway in use
			//check how many cards from 3 do we have
			for(int i=0; i<info.groups3; i++)
			{
				if(info.cards_3[i] == Cards[0]) 
					HandCardsFromCombination++;
				if(info.cards_3[i] == Cards[1])
					HandCardsFromCombination++;
			}

			if(HandCardsFromCombination < 2)
			{
				//check the highest 2 we have on hand
				for(int i=info.groups2-1; i<info.groups2; i++)
				{
					if(info.cards_2[i] == Cards[0]) 
						HandCardsFromCombination++;
					if(info.cards_2[i] == Cards[1])
						HandCardsFromCombination++;
				}
			}
		}
	}
}


//--------------------------------------------------------------------------------------------------------------------------------
bool AiBrain::DetermineCombination(char iTableCards[20][2], const int iSize)
{
	//too few cards
	if(iSize < 1) return false;


	char c[100];
	char s[100];
	int n = 0;
	int player_cards[2] = {0, 1};

	//fill and sort input sequence
	for(int i=0; i<2; i++)
	{
		c[i] = Cards[i];		
		s[i] = Suits[i];
		n++;
	}

	for(int i=0; i<iSize; i++)
	{
		c[i+2] = iTableCards[i][0];
		s[i+2] = iTableCards[i][1];
		n++;
	}

	SortCards(c, s, player_cards, n);
	CombinationInfo info = GatherCombinationInfo(c, s, n);


	//determine combination
	Combintation = COMB_NOTHING;
	ProcessFlashOrStreet(info, player_cards);	
	if(Combintation == COMB_NOTHING)
		Process234FullHouse(info, player_cards);
	

	//get result
	return  Combintation != COMB_NOTHING && 
			(Combintation >= COMB_FULLHOUSE || (Combintation < COMB_FULLHOUSE && HandCardsFromCombination > 0));
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DetermineFirstRound()
{
	//if cards count is changed - first round
	if(Pl->TableCache != 0 && Pl->TableInfo->Cards.size() != Pl->TableCache->Cards.size() )
	{
		IsFirstRound = true;
	}
	else if(FirstTime) //if the very first time we called - fist round
	{
		FirstTime = false;
		IsFirstRound = true;
	}
	else 
	{
		IsFirstRound = false;
	}

	//player cards is changed
	if(Pl->PlayerCache && Pl->PlayerCache[Pl->MyPos].Cards != Pl->PlayerInfo[Pl->MyPos].Cards)
		IsFirstRound = true;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::DetermineHand()
{
	Hand = HAND_NOTHING;

	//get highest board card
	char hc = '1';
	char tc[20][2];
	int tcsize = 0;
	for(size_t i=0, j=0; i<Pl->TableInfo->Cards.size(); i+=3, j++, tcsize++)
	{
		if(CardStack[Pl->TableInfo->Cards[i]] > CardStack[hc])
			hc = Pl->TableInfo->Cards[i];
		tc[j][0] = Pl->TableInfo->Cards[i];
		tc[j][1] = Pl->TableInfo->Cards[i+1];
	}

	//check for highest combinations
	if(DetermineCombination(tc, tcsize))
		Hand = HAND_TWO_PAIRS_OR_MORE;
	else
	{
		//check for top pair
		hc = toupper(hc);
		if(Pl->TableInfo->Cards.size() > 0 && (hc == Cards[0] || hc == Cards[1]))
			Hand = HAND_TOP_PAIR;
		else
		{
			//over-pair
			if(CardStack[hc] < CardStack[Cards[0]] && CardStack[hc] < CardStack[Cards[1]] && Cards[0] == Cards[1])
				Hand = HAND_OVER_PAIR;
		}
	}
}



//---------------------------------------------------------------------------------------------------------------------------------------
bool AiBrain::ValidateData()
{
	ErrMsg = _T("");

	if(!Pl) return false;
	if(!Pl->PlayerInfo || !Pl->TableInfo) return false;
	if(Pl->MyPos < 0 || Pl->MyPos > 9) return false;
	
	//check if active palyers > 1
	int numact = 0;
	for(int i=0; i<NUM_PLAYERS; i++)
	{
		if(Pl->PlayerInfo[i].Active) 
		{
			numact++;

			//check if active player have stack or stake
			if(Pl->PlayerInfo[i].Stack < 0 && Pl->PlayerInfo[i].Stake < 0)
			{
				ErrMsg += _T("One of active players have no stack and no stake\n");
				return false;
			}
		}
	}
	if(numact < 2)
	{
		ErrMsg += _T("Number of active players < 2\n");
		return false;
	}


	//check if we have cards and we are active
	if(false == Pl->PlayerInfo[Pl->MyPos].Active)
	{
		ErrMsg += _T("Our position has no player\n");
		return false;
	}
	if(Pl->PlayerInfo[Pl->MyPos].Cards.length() < 5)
	{
		ErrMsg += _T("Our palyer's cards have been not parsed\n");
		return false;
	}
	
	
	//check blinds
	if(Pl->TableInfo->BigBlind <= 0 && Pl->TableInfo->SmallBlind <= 0)
	{
		ErrMsg += _T("Bad blinds\n");
		return false;
	}


	//check pot
	if(Pl->TableInfo->Pot < 0)
	{
		ErrMsg += _T("Bad pot\n");
		return false;
	}


	//button pos
	if(Pl->TableInfo->ButtonPos < 0 || Pl->TableInfo->ButtonPos >= NUM_PLAYERS)
	{
		ErrMsg += _T("Bad button pos\n");
		return false;
	}

	return true;
}	


//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::Actions AiBrain::MakeDecision(AiPlayer *iPlayer, int &oRaiseCnt)
{
	Pl = iPlayer;

	if(!ValidateData())
	{
		if(ErrMsg == _T(""))
		{
			ErrMsg = _T("Something bad happened :(\n");		
		}
		return ACT_ERROR;
	}

	srand(GetTickCount());

	Combintation = COMB_NOTHING;
	HandCardsFromCombination = 0;

	//determine all :)
	DeterminePhase();
	DetermineStage();
	DetermineCards();
	DetermineHand();	
	DeterminePosition();
	DetermineFirstRound();
	DetermineRaises();
	
	

	Actions act;

	//get action
	if(Phase == PHASE_LATE)
		act = Tables[STAGE_PREFLOP]->GetAction(this, PreflopRaise, oRaiseCnt);
	else
		act = Tables[Stage]->GetAction(this, PreflopRaise, oRaiseCnt);
	

	//four + A on hand = all in
	if(Combintation == COMB_FOUR && HandCardsFromCombination == 0 && (Cards[0] == 'a' || Cards[1] == 'A'))
		act = ACT_ALL_IN;

	//raise or all in + flash + 0 card on hand = < 5BB  call
	if(act > ACT_CALL && Combintation == COMB_FLUSH && HandCardsFromCombination == 0)
	{
		if(oRaiseCnt > Pl->TableInfo->BigBlind * 5)
			act = ACT_FOLD;
		else
			act = ACT_CALL;
	}
	
	//remember raise on preflop
	if(Stage == STAGE_PREFLOP)
		PreflopRaise = act == ACT_RAISE;

	
	if(act == ACT_FOLD && Pl->TableInfo->MenuBtnCheck)
		act = ACT_CHECK;
	//can't raise
	if(act == ACT_CALL && !Pl->TableInfo->MenuBtnCall)
		act = ACT_ALL_IN;

	return act;
}


//--------------------------------------------------------------------------------------------------------------------------------
void AiBrain::GetMessage(TCHAR *oBuf)
{
	if(ErrMsg != _T(""))
	{
		_stprintf(oBuf, _T("%s"), ErrMsg);
		return;
	}

	//debug messages
	TCHAR *stages[STAGE_COUNT];
	stages[STAGE_PREFLOP] = _T("Stage: preflop;\n");
	stages[STAGE_FLOP] = _T("Stage: flop;\n");
	stages[STAGE_TURN] = _T("Stage: turn;\n");
	stages[STAGE_RIVER] = _T("Stage: river;\n");
	
	TCHAR *phases[PHASE_COUNT];
	phases[PHASE_EARLY] = _T("Phase: early;\n");
	phases[PHASE_MIDDLE] = _T("Phase: middle;\n");
	phases[PHASE_LATE] = _T("Phase: late;\n");

	TCHAR *hands[HAND_COUNT];
	hands[HAND_NOTHING] = _T("Hand: nothing;\n");
	hands[HAND_OVER_PAIR] = _T("Hand: over-pair;\n");
	hands[HAND_TOP_PAIR] = _T("Hand: top-pair;\n");
	hands[HAND_TWO_PAIRS_OR_MORE] = _T("Hand: two pairs or more;\n");

	TCHAR *combinations[COMB_COUNT];
	combinations[COMB_NOTHING] = _T("Combination: nothing or pair;\n");
	combinations[COMB_TWOPAIRS] = _T("Combination: two pairs;\n");
	combinations[COMB_THREE] = _T("Combination: three;\n");
	combinations[COMB_FOUR] = _T("Combination: four;\n");
	combinations[COMB_FLUSH] = _T("Combination: flush;\n");
	combinations[COMB_STREET] = _T("Combination: street;\n");
	combinations[COMB_FULLHOUSE] = _T("Combination: fulll house;\n");
	combinations[COMB_STREETFLASH] = _T("Combination: flash street;\n");
	combinations[COMB_ROYALFLASH] = _T("Combination: royal flash;\n");

	TCHAR *positions[POS_COUNT];
	positions[POS_BLINDS] = _T("Pos: blinds;\n");
	positions[POS_EARLY] = _T("Pos: early;\n");
	positions[POS_MIDDLE] = _T("Pos: middle;\n");
	positions[POS_LATE] = _T("Pos: late;\n");

	TCHAR *raises[POS_COUNT];
	raises[RAISE_NO] = _T("Raises before: no;\n");
	raises[RAISE_ONE] = _T("Raises before: one;\n");
	raises[RAISE_MORE_THAN_ONE] = _T("Raises before: more than one;\n");

	TCHAR *raises_after[2];
	raises_after[0] = _T("Raises after: no;\n");
	raises_after[1] = _T("Raises after: yes;\n");
	
	TCHAR *first_round[2];
	first_round[0] = _T("First round: false;\n");
	first_round[1] = _T("First round: true;\n");

	TCHAR cards_on_hand[200];
	_stprintf(cards_on_hand, _T("From combination on hand: %d;\n"), HandCardsFromCombination);
	
	_stprintf(oBuf, _T("%s%s%s%s%s%s%s%s%s"), combinations[Combintation], cards_on_hand, first_round[IsFirstRound], stages[Stage], phases[Phase], hands[Hand], positions[Pos], raises[Raise], raises_after[WasRaisesAfterUs]);
}


//--------------------------------------------------------------------------------------------------------------------------------
AiBrain::~AiBrain()
{
	for(int i=0; i<STAGE_COUNT; i++)
		delete Tables[i];
}
